package com.rankeiot.core.sqltoy;

import com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException;
import com.rankeiot.core.util.StringUtil;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.extern.slf4j.Slf4j;
import org.sagacity.sqltoy.SqlToyConstants;
import org.sagacity.sqltoy.config.annotation.Column;
import org.sagacity.sqltoy.config.annotation.Entity;
import org.sagacity.sqltoy.config.annotation.Id;
import org.sagacity.sqltoy.dao.SqlToyLazyDao;

import javax.sql.DataSource;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.*;
import java.util.*;
import java.util.function.BiConsumer;

/**
 *
 */

@Slf4j
public class TableUtil {

    private static final Set<Integer> lengthTypes = new HashSet<>(Arrays.asList(Types.VARCHAR, Types.CHAR, Types.LONGVARCHAR, Types.TINYINT));

    public static void hasTable(DataSource ds, String tableNamePattern, BiConsumer<Connection, String> tableNameConsumer) {
        try (Connection conn = ds.getConnection()) {
            DatabaseMetaData meta = conn.getMetaData();
            ResultSet tables = meta.getTables(conn.getCatalog(), conn.getSchema(), tableNamePattern, new String[]{"TABLE"});
            boolean hasTable = false;
            while (tables.next()) {
                hasTable = true;
                tableNameConsumer.accept(conn, tables.getString("TABLE_NAME"));
            }
            if (!hasTable) {
                tableNameConsumer.accept(conn, null);
            }
        } catch (CannotFindDataSourceException e) {
            log.warn("未初始化数据库", e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static void createTable(SqlToyLazyDao dao, Class entityClass) {
        try {
            String name = dao.getDataSource().getConnection().getMetaData().getDatabaseProductName();
            TableCreateSqlGenerator generator = DefaultTableCreateSqlGenerators.valueOf(name);
            String sql = generator.generate(entityClass);// getCreateSql( entityClass);
            dao.executeSql(sql, (Map<String, Object>) new HashMap<String, Object>());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }


    public interface TableCreateSqlGenerator {
        /**
         * 通过Entity生成建表SQL,仅供参考，如果需要生成表，建议执行编写建表SQL
         *
         * @param entityClass
         * @return
         */
        String generate(Class entityClass);
    }

    public enum DefaultTableCreateSqlGenerators implements TableCreateSqlGenerator {
        H2 {
            @Override
            public String generate(Class entityClass) {
                StringBuffer part = new StringBuffer();
                part.append("CREATE TABLE IF NOT EXISTS ");
                Entity meta = (Entity) entityClass.getAnnotation(Entity.class);
                part.append(meta.tableName());
                part.append("(");
                boolean first = true;
                Set<String> pks = new HashSet<>();
                for (Field field : entityClass.getDeclaredFields()) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        continue;
                    }
                    Column c = field.getAnnotation(Column.class);
                    if (c == null) {
                        continue;
                    }
                    if (first) {
                        first = false;
                        part.append("\n\t");
                    } else {
                        part.append(",\n\t");
                    }

                    part.append(c.name());
                    part.append(" ");

                    int type = c.type();
                    part.append(JDBCType.valueOf(type).getName());
                    if (lengthTypes.contains(type)) {
                        part.append("(").append(c.length()).append(") ");
                    }

                    if (field.getAnnotation(Id.class) != null) {
                        //  part.append(" PRIMARY KEY ");
                        part.append(" NOT NULL ");
                        pks.add("`" + c.name() + "`");
                    } else if (!c.nullable()) {
                        part.append(" NOT NULL ");
                    }

                    if (c.autoIncrement()) {
                        part.append(" AUTO_INCREMENT ");
                    }
                    if (!SqlToyConstants.DEFAULT_NULL.equals(c.defaultValue())) {
                        part.append(" DEFAULT '").append(c.defaultValue()).append("'");
                    }

                    if (StringUtil.isNotEmpty(c.comment())) {
                        part.append(" COMMENT '").append(c.comment()).append("' ");
                    } else {
                        Schema n = field.getAnnotation(Schema.class);
                        if (n != null && StringUtil.isNotEmpty(n.description())) {
                            part.append(" COMMENT '").append(n.description()).append("' ");
                        }
                    }

                }
                if (pks.size() > 0) {
                    part.append(", PRIMARY KEY (" + String.join(",", pks) + ")");
                }
                part.append("\n) DEFAULT CHARSET=utf8mb4");
                if (StringUtil.isNotEmpty(meta.comment())) {
                    part.append(" COMMENT ='").append(meta.comment()).append("' ");
                } else {
                    Schema n = (Schema) entityClass.getAnnotation(Schema.class);
                    if (n != null && StringUtil.isNotEmpty(n.description())) {
                        part.append(" COMMENT '").append(n.description()).append("' ");
                    }
                }
                part.append(";");
                return part.toString();
            }
        },
        MySQL {
            @Override
            public String generate(Class entityClass) {
                StringBuffer part = new StringBuffer();
                part.append("CREATE TABLE IF NOT EXISTS ");
                Entity meta = (Entity) entityClass.getAnnotation(Entity.class);
                part.append(meta.tableName());
                part.append("(");
                boolean first = true;
                Set<String> pks = new HashSet<>();
                for (Field field : entityClass.getDeclaredFields()) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        continue;
                    }
                    Column c = field.getAnnotation(Column.class);
                    if (c == null) {
                        continue;
                    }
                    if (first) {
                        first = false;
                        part.append("\n\t");
                    } else {
                        part.append(",\n\t");
                    }

                    part.append(c.name());
                    part.append(" ");

                    int type = c.type();
                    part.append(JDBCType.valueOf(type).getName());
                    if (lengthTypes.contains(type)) {
                        part.append("(").append(c.length()).append(") ");
                    }

                    if (field.getAnnotation(Id.class) != null) {
                        //  part.append(" PRIMARY KEY ");
                        part.append(" NOT NULL ");
                        pks.add("`" + c.name() + "`");
                    } else if (!c.nullable()) {
                        part.append(" NOT NULL ");
                    }

                    if (c.autoIncrement()) {
                        part.append(" AUTO_INCREMENT ");
                    }
                    if (!SqlToyConstants.DEFAULT_NULL.equals(c.defaultValue())) {
                        part.append(" DEFAULT '").append(c.defaultValue()).append("'");
                    }

                    if (StringUtil.isNotEmpty(c.comment())) {
                        part.append(" COMMENT '").append(c.comment()).append("' ");
                    } else {
                        Schema n = field.getAnnotation(Schema.class);
                        if (n != null && StringUtil.isNotEmpty(n.description())) {
                            part.append(" COMMENT '").append(n.description()).append("' ");
                        }
                    }

                }
                if (pks.size() > 0) {
                    part.append(", PRIMARY KEY (" + String.join(",", pks) + ") USING BTREE");
                }
                part.append("\n) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4");
                if (StringUtil.isNotEmpty(meta.comment())) {
                    part.append(" COMMENT ='").append(meta.comment()).append("' ");
                } else {
                    Schema n = (Schema) entityClass.getAnnotation(Schema.class);
                    if (n != null && StringUtil.isNotEmpty(n.description())) {
                        part.append(" COMMENT '").append(n.description()).append("' ");
                    }
                }
                part.append(";");
                return part.toString();
            }
        }
    }
}
